/*
 * MsgConn.cpp
 *
 *  Created on: 2013-7-5
 *      Author: ziteng@mogujie.com
 */

#include "MsgConn.h"
#include "ImPduBase.h"
#include "ImPduClient.h"

static ConnMap_t g_msg_conn_map;
static UserMap_t g_msg_conn_user_map;

void msg_conn_timer_callback(void* callback_data, uint8_t msg, uint32_t handle, void* pParam)
{
	ConnMap_t::iterator it_old;
	CMsgConn* pConn = NULL;
	uint64_t cur_time = get_tick_count();

	for (ConnMap_t::iterator it = g_msg_conn_map.begin(); it != g_msg_conn_map.end(); ) {
		it_old = it;
		it++;

		pConn = (CMsgConn*)it_old->second;
		pConn->OnTimer(cur_time);
	}
    
}

static void signal_handler_usr1(int sig_no)
{
	if (sig_no == SIGUSR1) {
		log("receive SIGUSR1 ");
	}
}

static void signal_handler_usr2(int sig_no)
{
	if (sig_no == SIGUSR2) {
		log("receive SIGUSR2 ");
	}
}

static void signal_handler_hup(int sig_no)
{
	if (sig_no == SIGHUP) {
		log("receive SIGHUP exit... ");
		exit(0);
	}
}

void init_msg_conn()
{
	signal(SIGUSR1, signal_handler_usr1);
	signal(SIGUSR2, signal_handler_usr2);
	signal(SIGHUP, signal_handler_hup);
	netlib_register_timer(msg_conn_timer_callback, NULL, 1000);
}

////////////////////////////
CMsgConn::CMsgConn()
{
    m_user_id = 0;
    m_bOpen = false;
}

CMsgConn::~CMsgConn()
{

}

void CMsgConn::Close()
{
    uint32_t nUserId = GetUserId();
    log("Close client, handle=%d, user_id=%u ", m_handle, nUserId);
    if (m_handle != NETLIB_INVALID_HANDLE) {
        netlib_close(m_handle);
        g_msg_conn_map.erase(m_handle);
    }
    if(nUserId != 0) 
    {
        g_msg_conn_user_map.erase(nUserId);
    }
    ReleaseRef();
}

void CMsgConn::OnConnect(net_handle_t handle)
{
	m_handle = handle;

	g_msg_conn_map.insert(make_pair(handle, this));

	netlib_option(handle, NETLIB_OPT_SET_CALLBACK, (void*)imconn_callback);
	netlib_option(handle, NETLIB_OPT_SET_CALLBACK_DATA, (void*)&g_msg_conn_map);
	netlib_option(handle, NETLIB_OPT_GET_REMOTE_IP, (void*)&m_peer_ip);
	netlib_option(handle, NETLIB_OPT_GET_REMOTE_PORT, (void*)&m_peer_port);
}

void CMsgConn::OnClose()
{
    log("Warning: peer closed. ");
	Close();
}

void CMsgConn::OnTimer(uint64_t curr_tick)
{

}

void CMsgConn::HandlePdu(CImPdu* pPdu)
{
	// request authorization check
	if (pPdu->GetCommandId() != IM_PDU_LOGIN_REQ && !IsOpen()) {
        log("HandlePdu, wrong msg. ");
        throw CPduException(pPdu->GetCommandId(), ERROR_CODE_WRONG_SERVICE_ID, "HandlePdu error, user not login. ");
		return;
    }
    
	switch (pPdu->GetCommandId()) {
        case IM_PDU_LOGIN_REQ:
            _HandleLogin(pPdu);
            break;
        case IM_PDU_INIT_CALL_REQ:
            _HandleInitCallReq(pPdu);
            break;
        case IM_PDU_CALL_NOTIFY_RES:
            _HandleCallNotifyRes(pPdu);
            break;
        case IM_PDU_HUNG_UP_REQ:
            _HandleHungUpReq(pPdu);
            break;
        case IM_PDU_HUNG_NOTIFY_RES:
            _HandleHungUpNotifyRes(pPdu);
            break;
        case IM_PDU_ONLINE_USER_REQ:
            _HandleOnlineUserReq(pPdu);
            break;
        default:
            log("wrong msg, cmd id=%d, user id=%u. ", pPdu->GetCommandId(), GetUserId());
            break;
	}
}

void CMsgConn::_HandleLogin(CImPdu *pPdu)
{
    CImPduLoginReq* pPduReq = (CImPduLoginReq*)pPdu;
    m_user_id = pPduReq->getUserId();
    g_msg_conn_user_map.insert(make_pair(m_user_id, this));
    m_bOpen = true;
    log("%d login\n", m_user_id);
    CImPduLoginRes cPdu(0);
    SendPdu(&cPdu);
}


void CMsgConn::_HandleInitCallReq(CImPdu *pPdu)
{
    CImPduInitCallReq* pPduReq = (CImPduInitCallReq*) pPdu;
    
    uint32_t nToId = pPduReq->getToId();
    
    auto it = g_msg_conn_user_map.find(nToId);
    string& strBody = pPduReq->getBody();
    if( it == g_msg_conn_user_map.end()) {
        CImPduInitCallRes cPdu(nToId, pPduReq->getCallId(), 1, "User not onlined", strBody.data(), strBody.length());
        SendPdu(&cPdu);
        return;
    }
    
    CImPduCallNotifyReq cPdu(m_user_id, pPduReq->getCallId(), strBody.data(), strBody.length());
    CMsgConn* pConn = (CMsgConn*)it->second;
    if (pConn) {
        pConn->SendPdu(&cPdu);
    }
    
    return;
}

void CMsgConn::_HandleCallNotifyRes(CImPdu *pPdu)
{
    CImPduCallNotifyRes* pPduReq = (CImPduCallNotifyRes*)pPdu;
    
    uint32_t nFromId = pPduReq->getFromId();
    string& strBody = pPduReq->getBody();
    auto it = g_msg_conn_user_map.find(nFromId);
    CMsgConn* pConn = (CMsgConn* )it->second;
    if(pConn) {
        CImPduInitCallRes cPdu(m_user_id, pPduReq->getCallId(), pPduReq->getCode(), pPduReq->getReason(), strBody.data(), strBody.length());
        pConn->SendPdu(&cPdu);
    }
    
}

void CMsgConn::_HandleHungUpReq(CImPdu *pPdu)
{
    CImPduHungUpReq* pPduReq = (CImPduHungUpReq*) pPdu;
    
    uint32_t nPeerId = pPduReq->getPeerId();
    uint32_t nCallId = pPduReq->getCallId();
    auto it = g_msg_conn_user_map.find(nPeerId);
    if(it == g_msg_conn_user_map.end()) {
        CImPduHungUpRes cPdu(nPeerId, nCallId);
        SendPdu(&cPdu);
        return;
    }
    
    CMsgConn* pConn = (CMsgConn*) it->second;
    if(pConn) {
        CImPduHungUpNotifyReq cPdu(m_user_id, nCallId);
        pConn->SendPdu(&cPdu);
    }
}


void CMsgConn::_HandleHungUpNotifyRes(CImPdu *pPdu)
{
    CImPduHungUpNotifyRes* pPduReq = (CImPduHungUpNotifyRes*) pPdu;
    
    uint32_t nPeerId = pPduReq->getPeerId();
    uint32_t nCallId = pPduReq->getCallId();
    
    auto it = g_msg_conn_user_map.find(nPeerId);
    if(it != g_msg_conn_user_map.end()) {
        CMsgConn* pConn = (CMsgConn*)it->second;
        
        CImPduHungUpRes cPdu(m_user_id, nCallId);
        if(pConn) {
            pConn->SendPdu(&cPdu);
        }
    }
}


void CMsgConn::_HandleOnlineUserReq(CImPdu* pPdu)
{
    CImPduOnlineUserReq* pPduReq = (CImPduOnlineUserReq*) pPdu;
    list<uint32_t> lsUsers;
    auto it = g_msg_conn_user_map.begin();
    for( ; it!= g_msg_conn_user_map.end(); ++it) {
        if (it->first != GetUserId()) {
            lsUsers.push_back(it->first);
        }
    }
    
    uint32_t nUserCnt = lsUsers.size();
    CImPduOnlineUserRes cPdu(nUserCnt, lsUsers);
    SendPdu(&cPdu);
}

